﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DomainService.cs" company="Microsoft">
//   Microsoft
// </copyright>
// <summary>
//   Service class to expose DomainContext methods
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace MySemanticSearch.Service
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ServiceModel.DomainServices.Client;

    using MySemanticSearch.Web.DataAccess;
    using MySemanticSearch.Web.Model;

    /// <summary>
    /// Service class to expose DomainContext methods
    /// </summary>
    public class DomainService : IDomainService
    {
        /// <summary>
        /// Related Documents Domain Context
        /// </summary>
        private readonly RelatedDocumentsContext relatedDocumentsDomainContext = new RelatedDocumentsContext();

        /// <summary>
        /// Documents Domain Context
        /// </summary>
        private readonly DocumentsContext documentsDomainContext = new DocumentsContext();

        /// <summary>
        /// SQL Server Management Domain Context
        /// </summary>
        private readonly SqlServerManagement sqlServerManagement = new SqlServerManagement();

        /// <summary>
        /// Queries the RIA service to get a collection of documents
        /// </summary>
        /// <param name="callback">
        /// The callback.
        /// </param>
        /// <param name="selectedSqlInstance">
        /// The selected sql instance.
        /// </param>
        /// <param name="selectedDatabase">
        /// The selected database.
        /// </param>
        /// <param name="selectedFileTable">
        /// The selected file table.
        /// </param>
        public void GetDocuments(Action<ObservableCollection<Document>, Exception> callback, string selectedSqlInstance, string selectedDatabase, string selectedFileTable)
        {
            this.documentsDomainContext.Documents.Clear();
            this.documentsDomainContext.Load(
                this.documentsDomainContext.GetDocumentsQuery(
                    selectedSqlInstance,
                    selectedDatabase,
                    selectedFileTable),
                lo => DocumentObservableCollectionCallback(lo, callback),
                null);
        }

        /// <summary>
        /// Queries the RIA service to get a collection of related documents
        /// </summary>
        /// <param name="callback">
        /// The viewModelCallbackMethod.
        /// </param>
        /// <param name="selectedSqlInstance">
        /// The selected sql instance.
        /// </param>
        /// <param name="selectedDatabase">
        /// The selected database.
        /// </param>
        /// <param name="selectedFileTable">
        /// The selected file table.
        /// </param>
        /// <param name="documentsSelectedItem">
        /// The documents selected item.
        /// </param>
        public void GetRelatedDocuments(Action<ObservableCollection<Document>, Exception> callback, string selectedSqlInstance, string selectedDatabase, string selectedFileTable, string documentsSelectedItem)
        {
            this.relatedDocumentsDomainContext.Documents.Clear();
            this.relatedDocumentsDomainContext.Load(
                this.relatedDocumentsDomainContext.GetRelatedDocumentsQuery(
                    selectedSqlInstance,
                    selectedDatabase,
                    selectedFileTable,
                    documentsSelectedItem),
                lo => DocumentObservableCollectionCallback(lo, callback),
                null);
        }

        /// <summary>
        /// Queries the RIA service to search for documents using a query string
        /// </summary>
        /// <param name="viewModelCallback">
        /// The view model callback.
        /// </param>
        /// <param name="selectedSqlInstance">
        /// The selected sql instance.
        /// </param>
        /// <param name="selectedDatabase">
        /// The selected database.
        /// </param>
        /// <param name="selectedFileTable">
        /// The selected file table.
        /// </param>
        /// <param name="query">
        /// The query.
        /// </param>
        public void GetSearchResults(Action<ObservableCollection<Document>, Exception> viewModelCallback, string selectedSqlInstance, string selectedDatabase, string selectedFileTable, string query)
        {
            this.documentsDomainContext.Documents.Clear();
            this.documentsDomainContext.Load(
                this.documentsDomainContext.GetSearchResultsQuery(
                    selectedSqlInstance,
                    selectedDatabase,
                    selectedFileTable,
                    query),
                lo => DocumentObservableCollectionCallback(lo, viewModelCallback),
                null);
        }

        /// <summary>
        /// Retrieves all used databases from the service
        /// </summary>
        /// <param name="viewModelCallbackMethod">
        /// The viewModelCallbackMethod method.
        /// </param>
        /// <param name="sqlInstance">
        /// The sql Instance.
        /// </param>
        /// <param name="hasFileTable">
        /// The has file table.
        /// </param>
        public void GetDatabaseNameList(Action<IEnumerable<string>, Exception> viewModelCallbackMethod, string sqlInstance, bool hasFileTable)
        {
            this.sqlServerManagement.GetDatabaseNameList(sqlInstance, hasFileTable, StringIEnumerableCallback, viewModelCallbackMethod);
        }

        /// <summary>
        /// Retrieves the list of FileTables for a particular database
        /// </summary>
        /// <param name="viewModelCallbackMethod">
        /// The viewModelCallbackMethod.
        /// </param>
        /// <param name="selectedSqlInstance">
        /// The Selected Sql Instance.
        /// </param>
        /// <param name="selectedDatabase">
        /// The selected database.
        /// </param>
        public void GetFileTableList(Action<IEnumerable<string>, Exception> viewModelCallbackMethod, string selectedSqlInstance, string selectedDatabase)
        {
            this.sqlServerManagement.GetFileTableList(selectedSqlInstance, selectedDatabase, StringIEnumerableCallback, viewModelCallbackMethod);
        }

        /// <summary>
        /// Retrieves a list of all search properties for a selected FileTable
        /// </summary>
        /// <param name="viewModelCallbackMethod">
        /// The view model callback method.
        /// </param>
        /// <param name="selectedSqlInstance">
        /// The selected sql instance.
        /// </param>
        /// <param name="selectedDatabase">
        /// The selected database.
        /// </param>
        /// <param name="selectedFileTable">
        /// The selected file table.
        /// </param>
        public void GetSearchProperties(Action<IEnumerable<string>, Exception> viewModelCallbackMethod, string selectedSqlInstance, string selectedDatabase, string selectedFileTable)
        {
            this.sqlServerManagement.GetSearchProperties(selectedSqlInstance, selectedDatabase, selectedFileTable, StringIEnumerableCallback, viewModelCallbackMethod);
        }

        /// <summary>
        /// Retrieves tags for the database
        /// </summary>
        /// <param name="viewModelCallbackMethod">
        /// The view model callback method.
        /// </param>
        /// <param name="selectedSqlInstance">
        /// The selected sql instance.
        /// </param>
        /// <param name="selectedDatabase">
        /// The selected database.
        /// </param>
        /// <param name="selectedFileTable">
        /// The selected file table.
        /// </param>
        /// <param name="sliderTopMaxValue">
        /// The slider top max value.
        /// </param>
        public void GetTags(Action<Dictionary<string, double>, Exception> viewModelCallbackMethod, string selectedSqlInstance, string selectedDatabase, string selectedFileTable, int sliderTopMaxValue)
        {
            this.documentsDomainContext.GetTags(selectedSqlInstance, selectedDatabase, selectedFileTable, sliderTopMaxValue, StringDoubleDictionaryCallback, viewModelCallbackMethod);
        }

        /// <summary>
        /// Retrieves tags for a set of documents
        /// </summary>
        /// <param name="viewModelCallbackMethod">
        /// The view model callback method.
        /// </param>
        /// <param name="selectedSqlInstance">
        /// The selected sql instance.
        /// </param>
        /// <param name="selectedDatabase">
        /// The selected database.
        /// </param>
        /// <param name="selectedFileTable">
        /// The selected file table.
        /// </param>
        /// <param name="selectedDocuments">
        /// The selected documents.
        /// </param>
        /// <param name="sliderTopMaxValue">
        /// The slider top max value.
        /// </param>
        public void GetTagsForDocuments(Action<Dictionary<string, double>, Exception> viewModelCallbackMethod, string selectedSqlInstance, string selectedDatabase, string selectedFileTable, List<string> selectedDocuments, int sliderTopMaxValue)
        {
            this.documentsDomainContext.GetTagsForDocuments(selectedSqlInstance, selectedDatabase, selectedFileTable, selectedDocuments, sliderTopMaxValue, StringDoubleDictionaryCallback, viewModelCallbackMethod);
        }

        /// <summary>
        /// Downloads a specific document from the RIA service
        /// </summary>
        /// <param name="viewModelCallbackMethod">
        /// The view model callback method.
        /// </param>
        /// <param name="selectedSqlInstance">
        /// The selected sql instance.
        /// </param>
        /// <param name="selectedDatabase">
        /// The selected database.
        /// </param>
        /// <param name="selectedFileTable">
        /// The selected file table.
        /// </param>
        /// <param name="documentGuid">
        /// The document guid.
        /// </param>
        public void DownloadDocument(Action<byte[], Exception> viewModelCallbackMethod, string selectedSqlInstance, string selectedDatabase, string selectedFileTable, string documentGuid)
        {
            this.documentsDomainContext.DownloadDocument(selectedSqlInstance, selectedDatabase, selectedFileTable, documentGuid, ByteArrayCallback, viewModelCallbackMethod);
        }

        /// <summary>
        /// Deletes a specific document from the FileTable
        /// </summary>
        /// <param name="viewModelCallbackMethod">
        /// The view model callback method.
        /// </param>
        /// <param name="selectedSqlInstance">
        /// The selected sql instance.
        /// </param>
        /// <param name="selectedDatabase">
        /// The selected database.
        /// </param>
        /// <param name="selectedFileTable">
        /// The selected file table.
        /// </param>
        /// <param name="documentGuid">
        /// The document guid.
        /// </param>
        public void DeleteDocument(Action<bool, Exception> viewModelCallbackMethod, string selectedSqlInstance, string selectedDatabase, string selectedFileTable, string documentGuid)
        {
            this.documentsDomainContext.DeleteDocument(selectedSqlInstance, selectedDatabase, selectedFileTable, documentGuid, BooleanCallback, viewModelCallbackMethod);
        }

        /// <summary>
        /// Uploads a document to the FileTable
        /// </summary>
        /// <param name="viewModelCallbackMethod">
        /// The view model callback method.
        /// </param>
        /// <param name="selectedSqlInstance">
        /// The selected sql instance.
        /// </param>
        /// <param name="selectedDatabase">
        /// The selected database.
        /// </param>
        /// <param name="selectedFileTable">
        /// The selected file table.
        /// </param>
        /// <param name="document">
        /// The document.
        /// </param>
        /// <param name="fileName">
        /// The file name.
        /// </param>
        public void UploadDocument(Action<bool, Exception> viewModelCallbackMethod, string selectedSqlInstance, string selectedDatabase, string selectedFileTable, byte[] document, string fileName)
        {
            this.documentsDomainContext.UploadDocument(selectedSqlInstance, selectedDatabase, selectedFileTable, document, fileName, BooleanCallback, viewModelCallbackMethod);
        }

        /// <summary>
        /// Obtains a collection of documents that share a common tag
        /// </summary>
        /// <param name="viewModelCallbackMethod">
        /// The view model callback method.
        /// </param>
        /// <param name="selectedSqlInstance">
        /// The selected sql instance.
        /// </param>
        /// <param name="selectedDatabase">
        /// The selected database.
        /// </param>
        /// <param name="selectedFileTable">
        /// The selected file table.
        /// </param>
        /// <param name="tag">
        /// The tag param.
        /// </param>
        public void GetDocumentsByTag(Action<ObservableCollection<Document>, Exception> viewModelCallbackMethod, string selectedSqlInstance, string selectedDatabase, string selectedFileTable, string tag)
        {
            this.documentsDomainContext.Documents.Clear();
            this.documentsDomainContext.Load(
                this.documentsDomainContext.GetDocumentsByTagQuery(
                    selectedSqlInstance,
                    selectedDatabase,
                    selectedFileTable,
                    tag),
                lo => DocumentObservableCollectionCallback(lo, viewModelCallbackMethod),
                null);
        }

        /// <summary>
        /// Callback method for any method in the DomainContext that returns a boolean
        /// </summary>
        /// <param name="args">
        /// The callback arguments.
        /// </param>
        private static void BooleanCallback(InvokeOperation<bool> args)
        {
            var callbackMethod = (Action<bool, Exception>)args.UserState;
            if (args.HasError)
            {
                args.MarkErrorAsHandled();
            }

            callbackMethod(args.Value, args.Error);
        }

        /// <summary>
        /// Callback method for the DomainContext that handles a byte array containing a document's content
        /// </summary>
        /// <param name="args">
        /// The callback arguments.
        /// </param>
        private static void ByteArrayCallback(InvokeOperation<byte[]> args)
        {
            var callbackMethod = (Action<byte[], Exception>)args.UserState;
            if (args.HasError)
            {
                args.MarkErrorAsHandled();
            }

            callbackMethod(args.Value, args.Error);
        }

        /// <summary>
        /// Callback method for the DomainContext that handles a dictionary of tags with their correspondant score
        /// </summary>
        /// <param name="args">
        /// The callback arguments.
        /// </param>
        private static void StringDoubleDictionaryCallback(InvokeOperation<Dictionary<string, double>> args)
        {
            var callbackMethod = (Action<Dictionary<string, double>, Exception>)args.UserState;
            if (args.HasError)
            {
                args.MarkErrorAsHandled();
            }

            callbackMethod(args.Value, args.Error);
        }

        /// <summary>
        /// Callback method for the DomainContext that handles a collection of documents
        /// </summary>
        /// <param name="loadOperation">
        /// The load Operation.
        /// </param>
        /// <param name="callback">
        /// The callback.
        /// </param>
        private static void DocumentObservableCollectionCallback(LoadOperation<Document> loadOperation, Action<ObservableCollection<Document>, Exception> callback)
        {
            if (loadOperation.HasError)
            {
                loadOperation.MarkErrorAsHandled();
            }

            callback.Invoke(new ObservableCollection<Document>(loadOperation.Entities), loadOperation.Error);
        }

        /// <summary>
        /// Callback method for the DomainContext that handles a list of strings
        /// </summary>
        /// <param name="args">
        /// The callback argument.
        /// </param>
        private static void StringIEnumerableCallback(InvokeOperation<IEnumerable<string>> args)
        {
            var callbackMethod = (Action<IEnumerable<string>, Exception>)args.UserState;
            if (args.HasError)
            {
                args.MarkErrorAsHandled();
            }

            callbackMethod(args.Value, args.Error);
        }
    }
}
